home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-02
/
pnl006.zip
/
PNL006.TXT
< prev
next >
Wrap
Text File
|
1991-03-17
|
64KB
|
1,692 lines
//////// // // //
// // /// // //
// // //// // //
//////// // // // //
// // //// //
// // /// //
// // // ///////
Pascal NewsLetter
Issue #6
March, 1991
Editor: Pete Davis
The Programmer's Forum BBS is the home of
PNL. It can be reached in Washington, DC at
(202)966-3647. Information is available
through the following locations:
FidoNet: Pete Davis@1:109/138
GEnie: P.DAVIS5
BitNet: HJ647C@GWUVM & UE356C@GWUVM
InterNet: HJ647C@GWUVM.GWU.EDU
or UE356C@GWUVM.GWU.EDU
Uucp: Pete.Davis@f138.n109.z1.fidonet.org
Table of Contents
Introduction ................................ 3 (P.D.)
Returning Structured Data Using TP Functions. 5 (B.M.)
Displaying an Integer with Commas, Revisited. 10 (N.P.)
Chess-Ter ................................... 13 (P.D.)
Searching ................................... 19 (K.S.)
For Beginners ............................... 21 (B.G.)
Conclusion .................................. 26 (P.D.)
Key:
----
P.D. - Pete Davis : Editor-in-Chief
R.M. - Richard Morris : Editor-Over-The-Pond
B.G. - Bob Gowans : Columnist
B.M. - Bill Madison : Contributing Writer
N.P. - Nathan Phillips : Contributing Writer
K.S. - Kelly Schwarzhoff : Contributing Writer
Introduction
Well, it's been quite a while since PNL005 and I apologize for
the delay. Many of you know why it took so long, and for those of you
who don't, I will give you my excuse. First of all, since this is
going to remain a free newsletter in the foreseeable future, I have to
put my priorities in order. Since money isn't going to be my priority,
school is. I have decided to add to my course load so that I can
graduate a semester earlier than planned. This has resulted in the
addition of two credits bringing my total to 17. Also, I decided (not
wisely) to take a graduate course. The additional two credits and the
graduate course have taken up an enormous amount of extra time. On top
of this I also work 20-30 hours a week. These, combined with an
attempt to maintain my social life (very hard with school and work)
has left little time for keeping the newsletter coming out at a
regular interval. Right now I am in my spring break which will allow
me a little time to get this issue out. I hope to get started on
PNL007 before summer, but I just can't be sure. If the first half of
this semester is any indication, PNL007 won't come out until about a
week after summer vacation begins. Also, getting the issues out over
summer won't be as easy as last summer as I am going to be taking some
classes over the summer and working almost full time.
Well, that's my excuse. Don't get me wrong, I love doing the
newsletter, and if I didn't have to worry about school and work I
would certainly be working much harder on it.
I would like to bring up some very good news, though. One of my
professors, Ray Thomas, who was my professor for some of my first
classes in programming, will be retiring soon. Professor Thomas taught
me a lot of what I know and is directly or indirectly responsible for
several articles that I have written. He has also been very supportive
of the newsletter. He has told me that after his retirement (the end
of this semester) he will be writing articles now and then for the
newsletter. This is very good news to me and I think many of you will
be pleased.
The introduction and conclusion areas are sort of my areas to
ramble on about things related to the newsletter as opposed to Pascal
specific things. So, to that end, here are some things I want to talk
about. First of all: I love getting comments and suggestions about the
newsletter. (Not quite as much as getting articles, but the comments
help a lot.) So, keep them coming. Also, I've been thinking about
dividing the newsletter into sections. Maybe having a beginners,
moderate and advanced level areas. I'd like to hear comments on this
idea. Would it help you find the stuff you want a little quicker?
I feel like I have a million things to talk about. It has been so
long since my last issue, but I would probably bore many of you. I
will just add a little about what you can expect from this issue and
then let you go on to the good stuff.
- 3 -
This issue I will start my series on Chess-Ter, a chess game
written by me and 5 others. It's not an artificial intelligence chess
program, but one that two people can play. I present it for it's
application to Pascal, not AI. The first two or three installments of
this article aren't going to go very in-depth and will only get the
surface of certain parts of the program. The idea being that you, the
reader, will tell me what parts interest you and confuse you, then in
later issues I will go back and cover things in a more in-depth
fashion. Just because of the size of the program, it is going to take
a long time cover the whole thing and I want to get the whole program
out to you, the readers, as quickly as possible. Anyway, read the
article for more information.
Bill Madison presents a method of Returning Structured Data Using
Pascal Functions. Also, we have Displaying an Integer with Commas,
Revisited. This piece, by Nathan S. Phillips is a return to a
procedure that appeared in issue #2 of the PNL. He goes into new ways
of doing the same thing and showing the good and bad aspects of each
method. This is a great lesson in program optimization. Kelly
Schwarzhoff, a high school sophmore, shows some methods of searching.
a list. And Bob Gowans, my faithful companion comes through again with
another column in the For Beginners section.
Richard Morris' article has been dis-continued temporarily. We
hope to have it continued as soon as possible.
Well, that's about it, hope you enjoy this issue.
- 4 -
Returning Structured Data Using Turbo Pascal Functions
by Bill Madison
W. G. Madison & Associates, Ltd.
8425 Greenbelt Road, #101
P. O. Box 898
Greenbelt, MD 20770
(301)552-7234
CIS 73240,342
ABSTRACT
Turbo Pascal is, without question, the development language of
choice for microcomputer applications in which portability is not an
overriding issue (C addicts to the contrary notwithstanding).
Turbo Pascal, or any dialect of Pascal for that matter, has
several serious shortcomings. One of the more important among these is
Pascal's inability to establish structured data types (with the sole
exception of strings) as a function return. This effectively precludes
the writing of many types of packages in a programmer-friendly manner.
This article explores a technique for circumventing the problem
by using the mechanism of functions having returns of type POINTER or
a variant thereof. Examples are shown, drawn from a COMPLEX unit which
has been designed, written, and used by myself.
INTRODUCTION AND MOTIVATION
I was recently given an assignment by a client which involved
extensive manipulation of complex numbers. This was not the first time
this had occurred.
Tired of re-inventing the wheel every time it occurred, and
equally tired of making interminable procedure calls to evaluate a
simple expression, he decided that there had to be a better way. IF
ONLY TURBO PASCAL WOULD PERMIT FUNCTIONS TO RETURN STRUCTURED DATA
TYPES!!! The result turned out even better than expected; a design
methodology emerged which, when implemented, would work equally well
for ANY structured data type. With minor modification, it would even
work well with data types in which,ideally, the size of the actual
data to be manipulated could not be determined until execution time;
e.g., matrices, long strings (i.e., strings whose length exceeds 255
characters).
To refresh your memory, complex numbers, which will be used to
develop this discussion, are numbers of the form R + (i*I), where i =
sqrt(-1)). In a Pascal environment, this inherently represents a
RECORD structure:
type
ComplexBaseType = record
Re,
- 5 -
Im : real;
end; {ComplexBaseType}
Mathematical rules exist which define the four basic arithmetic
operations with respect to the complex numbers. Given these rules,
other rules have been derived such that, if an operation exists for
conventional REAL numbers an analogous rule exists for the COMPLEX
numbers. Thus, one may speak of powers and roots of complex numbers,
their trigonometric functions, etc.
In order to define these operations to Pascal, they would either
need to be defined in the native compiler or the compiler would need
to be syntax extensible. Then one would be able to write, for example,
C := A + B;
where A, B, C have been declared to be type ComplexBaseType.
Yet it is impossible to expect that the plethora of unusual data
types which one might devise could be compiled in native mode.
Further, syntax extensibility is a compiler attribute not found
(unfortunately) in commercially available compilers. (The most recent
commercially available compiler having any form of syntactic
extensibility which I have seen was the FORTRAN for the CDC 1604,
built during the mid-1960's.)
An alternative, therefore, would be to permit the user to write
functions implementing desired operations, which would return
arbitrary data types as function results. Were this language feature
available, and assuming that functions to combine two complex numbers
were declared
function CAdd(A, B : ComplexBaseType) : ComplexBaseType;
function CSub(A, B : ComplexBaseType) : ComplexBaseType;
function CMul(A, B : ComplexBaseType) : ComplexBaseType;
function CDiv(A, B : ComplexBaseType) : ComplexBaseType;
one could write the equivalent of
E := (A + B * C) / D
as
E := CDiv(CAdd(A, CMul(B, C)), D);
Clumsy as this might appear at first, it is infinitely more
readable (in my opinion) than declarations of
procedure CAdd(A, B : ComplexBaseType;
var C : ComplexBaseType);
procedure CSub(A, B : ComplexBaseType;
var C : ComplexBaseType);
- 6 -
procedure CMul(A, B : ComplexBaseType;
var C : ComplexBaseType);
procedure CDiv(A, B : ComplexBaseType;
var C : ComplexBaseType);
and an evaluation of the expression as
CMul(B, C, Temp);
CAdd(A, Temp, Temp);
CDiv(Temp, D, E);
especially as one attempts to evaluate more complex expressions.
Of course, we are still not out of the woods, because Turbo
Pascal can't return structured values from functions!
ONE POSSIBLE SOLUTION
Suppose, instead of working with ComplexBaseType as defined
above, we work with
type
Complex = ^ComplexBaseType;
and then in addition to declaring our variables
var
A, B, C, D, E : Complex;
we declare the functions as
function CAdd(A, B : Complex) : Complex;
function CSub(A, B : Complex) : Complex;
function CMul(A, B : Complex) : Complex;
function CDiv(A, B : Complex) : Complex;
then we can write the above expression evaluation as
E^ := CDiv(CAdd(A, CMul(B, C)), D)^;
which, as we can see, is just as readable and convenient as the
original (with the minor inconvenience of requiring the "^" symbol on
each side of the replacement operator.
Note, however, that since all operations are being performed by
nested function calls, the normal rules of operator precedence are not
applicable. All "operators" are of equal precedence. It is therefore
incumbent on the programmer to assure that any required operator
dependency is properly managed.
If this approach of using pointer-type functions is taken, an
additional step must be included. Note that a variable of type Complex
- 7 -
is actually a pointer. Unless a pointer is initialized with something
to point to, strange things will occur very quickly when the program
is executed. Therefore, each variable, in addition to being declared
must be initialized.
Pointer variables suggest (but do not require) that the things
pointed to must be on the heap. Assume that we accept the suggestion,
and agree to store all Complex data on the heap. Further, assume a
function CInit, declared as
function CInit(A : Complex) : boolean;
which will return FALSE if and only if there is insufficient
contiguous space left on the heap. Then, prior to the first reference
to any of the variables, we would have a sequence of instructions like
if not CInit(A) then {Do error recovery};
if not CInit(B) then {Do error recovery};
if not CInit(C) then {Do error recovery};
if not CInit(D) then {Do error recovery};
if not CInit(E) then {Do error recovery};
One additional requirement must be met to use this approach
effectively; specifically, the storage of intermediate results. When a
value is returned from a function in Turbo Pascal, that value is
stored on a stack and is popped off as required. The stack maintenance
code is generated by the compiler, as part of the compilation process.
However, when we take over a part of the compiler's functionality, as
we are doing here, we must also assume responsibility for keeping
track of the structured values being returned. This is necessitated by
the fact that the function returns are pointers to the returned
values, and not the values themselves!
This is most conveniently done by establishing one or more ring
buffers, again probably (but not necessarily) on the heap. The actual
structured value is then stored in the ring buffer, and the return
from the Pascal function becomes the pointer value pointing into the
ring. The size (i.e., the number of elements) of the ring buffer
determines the maximum complexity of the expression to be evaluated.
Theoretically it would be possible to write expressions whose
evaluation would require storage of any desired number of temporary
results. A more serious threat, however, lies in the evaluation of
recursive procedure or function calls, in which a stack (or ring) of
any size could be quickly exhausted.
CONCLUSION
While no perfect solution exists for the easy manipulation of
structured data types within the confines of any dialect of the PASCAL
programming language, a methodology has been suggested which, when
properly applied, can approach this ideal.
- 8 -
The use of Pascal functions which accept and return pointer
values and allocate data storage on the heap provides a mechanism for
effectively and relatively easily and readably manipulating data
structures of any degree of complexity.
A WORKING EXAMPLE
Following is the code for a unit I wrote to implement COMPLEX
arithmetic using Turbo Pascal, along with the code for a program used
to test the unit. The code for both the unit and the test program is
available on many BBSs under the name SHCMPLX1.ZIP.
I have also used this basic technique to implement a unit for the
manipulation of long strings (up to 65517 characters), without having
to allocate the full maximum allowable length for each long string
declared. The code for both the LongString unit and its test program
is available on many BBSs under the name SHLNGST1.ZIP.
{Editor's note: The file SHCMPLX1.ZIP is included with this PNL006.ZIP
file and the code mentioned in the article is found there.}
- 9 -
Displaying an Integer with Commas, Revisited
by Nathan S. Phillips
In the second issue of the PNL, I discussed a recursive method of
displaying an integer with commas (12345 -> 12,345). The recursive
procedure provided as an example at that time has several problems.
First, being recursive, stack space is used for storage of
parameter values and calls. The program has to keep track of all
local variable values used in the procedure so that they can be
restored when the next call returns. It takes time to perform these
housekeeping tasks. Although in this particular example these are not
major concerns, you should bear them in mind when contemplating more
complex recursive routines.
The second problem with the procedure is that it is fairly
cryptic; anyone attempting to revise or debug the code would probably
spend several minutes figuring out how the blasted thing works in the
first place. More than likely, the person would then choose to
rewrite the entire procedure, resulting in more lost time. As with
the first point, this is not a major problem with the particular
routine being considered, but with a larger piece of code the amount
of wasted time could add up very quickly.
Finally, since the procedure does the output internally, you must
position the cursor before calling the procedure; the number with
commas is then simply 'spit out' at that location. This makes it very
difficult to do formatting such as right justification, because you
don't know in advance (without doing additional calculations) how long
the number will be once the commas are included. Also, to get the
output to go somewhere besides the display, you have to modify the
procedure! Not good programming practice at all. If it returned a
string, we could then put the string wherever we want to, and with a
simple Length() call, we would know how long it is.
Wouldn't it be nice if this were a non-recursive, easily modified
function which took the number as a parameter and returned a string
which was the number with commas included? Well, let's make it so:
function NumWithCommas(theNum : longint) : string;
var nwcStr, { the working string }
tempStr { a temporary string }
: string;
begin { function NumWithCommas }
nwcStr := ''; { initialize working string }
while theNum >= 1000 do { as long as we need more commas }
begin { while theNum }
- 10 -
Str(theNum mod 1000, tempStr); { put the last 3 digits }
{ of theNum in tempStr }
while Length(tempStr) < 3 do { pad tempStr with }
tempStr := '0' + tempStr; { '0's on the left }
nwcStr := ',' + tempStr + nwcStr; { add comma & digits }
{ to working string }
theNum := theNum div 1000; { discard the 3 digits }
{ we just took care of }
end; { while theNum }
Str(theNum,tempStr); { now theNum is less than 1,000 }
NumWithCommas := tempStr + nwcStr; { put first digits }
{ at the beginning }
end; { function NumWithCommas }
This function has very little overhead, is easier to revise and
debug, and is generally more useful.
(Sidebar to technically inclined: I tested both forms of this
routine using Turbo Profiler and found, to my surprise, that the
non-recursive function version shown here is faster by a small amount.
I was sure that the string operations it uses would make it
considerably slower - any ideas what this isn't so? I forced this
function to write the result before returning, so the 'output
slowdown' should have been the same for both routines.)
Now, how does one go about using this new function? Suppose you
wish to inform the user of the number of bytes that the current
directory is taking up on the hard disk. A typical method is:
writeln(bytesUsed:1, ' bytes used for directory ',dirName);
which produces:
1348857 bytes used for directory FOO
With NumWithCommas, you can dress up the output very easily:
writeln(NumWithCommas(bytesUsed),' bytes used for directory',
dirName);
which produces:
1,348,857 bytes used for directory FOO
- 11 -
But the old recursive procedure could do that! Why did we go to
the trouble of rewriting it? Suppose there are several directories
for which you want to display size information in column format. With
the old DisplayWithCommas, you had to settle for:
for i := 1 to numDirs do
begin
for j := 1 to 8-Length(dirName[i]) do
write(' ');
write(dirName[i],' - ');
DisplayWithCommas(bytesUsed[i]);
writeln(' bytes');
end;
which produces:
FOO - 1,348,857 bytes
LABELS - 243,567 bytes
UTIL - 16,344,059 bytes
FAKEDIR - 1,024 bytes
Now with NumWithCommas as a function, you can do this:
for i := 1 to numDirs do
begin
for j := 1 to 8-Length(dirName[i]) do
write(' ');
write(dirName[i],' - ');
wcStr := NumWithCommas(bytesUsed[i]);
for j := 1 to 10-Length(wcStr) do
write(' ');
writeln(wcStr,' bytes');
end;
producing:
FOO - 1,348,857 bytes
LABELS - 243,567 bytes
UTIL - 16,344,059 bytes
FAKEDIR - 1,024 bytes
The combination of commas and formatting makes it very easy for
the user to determine at a glance the relative magnitude of each
value. And the easier your program is for the user, the more likely
it is to be used (and, hopefully, paid for).
- 12 -
Chess-Ter
By Pete Davis
Well, I mentioned in a previous issue that I would do this
article and, after a good deal of procrastination, I finally got
started on it. Part of the delay is just due to the volume of the
program itself. The code is broken into 8 units and comes to over 4800
lines of code. This sheer volume makes it a little difficult to handle
in the newsletter. I have decided to break up the article into either
two or three parts, initially. The two or three parts would be sort of
a quick overview and between those two or three, the entire source
code would be released.
For example, in this issue, I plan on talking about the
CHESSTER.PAS, GLOBALS.PAS, KB1.PAS and DRAW_PCS.PAS. I will only
release the source code for those parts of the program with this
issue. Then, in the next issue, I will release the source code to the
other parts of the program I discuss and, if necessary, I will release
anything remaining in the third part.
I am doing a rather brief overview at first so that I can get out
the different parts of the program quickly. After the entire program
has been released, I will back-track and talk about some of the more
technical aspects of the program and discuss ways of improving the
code and so-forth.
First, I suppose I should provide some sort of background on
Chess-Ter. Last semester I took a class called Software Engineering.
The main thrust of the class was putting together large projects. I've
thought about concentrating on that issue in a series in the
newsletter, but unless I get some requests, I'm going to hold off on
that. Anyway, about two weeks into the semester we got our assignment,
which was due at the end of the semester. The project was a Chess
program that two people could play. This is not an AI chess program,
it only lets two people play. The AI part is left for you, if you
dare.
Anyway, I got into a group of 5 other programmers who turned out
to be very hard working. Good thing, because I'm not and without the
push from the rest of the group, a project like this would not have
been done by me. So, I would first like to name all the authors and
point out their contributions:
Mark Friedman - Did most of the chess pieces and the board. He also
did the Handle Moves Request and Handle Take-Back Request.
Jessica Chestnut - Did most of the checks for legal moves. (A very
impressive feat for someone who had never played chess until a couple
days before the project was completed.)
Akiko Okamoto - Did the Load and Save games file handling.
- 13 -
John Loux - Wrote the main keyboard interface and the central
procedure of the program.
Paul Sternal - Wrote the Replay procedures that allow for game
replays.
and Me - I did the Check for Check and Check for Check-Mate
procedures. I also did the intro screen and the screen interface.
(With the exception of the board and pieces.)
That about covers the intro to the project. There are a few
things I'd like to bring up, though. First of all, the program is
copyrighted and the source code is released not just from me, but from
the entire group (collectively known as The Pascal Team). Also, I had
considered cleaning the program up before writing this, but then it
occurred to me that leaving in some of the problems might be useful in
discussion of improvements for the program. I hope to bring some
improvements into the program through future issues.
Well, let's get started. First of all, I guess I should discuss
the main program file, CHESSTER.PAS. Not a whole lot to discuss. It
includes all the necessary units: Globals (discussed later), CRT, KB1
(discussed later), DOS, and Draw_Pcs. The screen is initialized, the
game is initialized, the board is drawn, text displayed and finally
Get_Keyboard is called, which is an iterative procedure that keeps
going until the game is quit. Then the endprompt is shown. Not a whole
lot in the main program.
For large projects it's a good idea to keep the main program
itself rather small. This helps to enforce the idea of modularity, or
breaking your programs into smaller pieces.
The first unit I want to discuss is GLOBALS. GLOBALS contained
all the type definitions and procedures which were common throughout
the program. I want to point out that this wasn't always the case.
This was the desired result, but sometimes it turned out that a
procedure we thought would be used globally, turned out only to be
used one place, or vica-versa. The representation of our board was, of
course, a key concern, but even more important was the current state
of the game. We felt the best way for all the different procedures to
share information was to create a variable type of Game_State. The
Game_State had everything from a 2 dimensional array of all the pieces
on the board to the current move number, whose move it was and what
mode the game was playing in. (A short note about the modes. The game
has a novice and expert mode. The novice mode allows the ability to
take back moves and find out what all the legal moves for a given
piece are.) The game state also kept various flags about current
moves.
Along with the game_state there is also a move history which
keeps track of all the moves made. This is basically an array of the
Move_Type. The Move_Type contains the pieces from and to locations,
- 14 -
the piece color, the piece_type, what kind of piece was taken, if any,
and what the move type was (i.e. castle, check, mate, and other
exceptional conditions).
Although it is usually considered bad practice to keep global
variables, in this case we found it necessary and sometimes it is. We
kept several things global: The current game state, the current move,
the move_history and the list of legal moves for the current moving
piece.
Now I'd like to get into the graphics a bit. Here is something
that is mentioned in the Turbo manual and has turned out to be a very
useful feature. Instead of having the BGI files we needed loaded at
run-time, we decided to have them linked in at compile-time. This has
several advantages: First of all, it keeps you from having to keep
track of more than one file in the run-time program. It also saves a
bit of trouble, I feel.
In this program we used EGA mode. This was done mainly because
that was the only graphics I had and since I did a lot of the
graphics, I had to have it compatible with my system. So, here's how
we handle the compile-time linking of BGI files. (I also did this with
the Gothic character set included with TP.) Since we are working with
EGA, used the EGAVGA.BGI file. I changed this to an object file using
BINOBJ.EXE. I then typed, from the DOS prompt, with EGAVGA.BGI and
BINOBJ.EXE in the current directory:
BINOBJ EGAVGA.BGI EGAVGA.OBJ EGAVGA
This turns the .BGI into a .OBJ and gives it a public name of
EGAVGA (Don't worry about that last part, just stick to using the part
of the filename before the period and you should be fine.) I followed
this by doing the same thing to the gothic character set:
BINOBJ GOTH.CHR GOTH.OBJ GOTH
This turns the .CHR file into a .OBJ file also. Then in the
GLOBALS procedure I link both of these .OBJ files into the main
program as follows:
procedure EGAVGA; external;
{ The procedure name must match the Public name given above. }
{$L EGAVGA.OBJ}
{ the $L, above actually links it in. }
procedure GOTH; external;
{ Just like above: }
{$L GOTH.OBJ}
- 15 -
That's all there is to it. You now have the .BGI files linked in.
Now, to actually use those .BGI routines you need to register them
with TP. This is all done in the InitScrn procedure in the GLOBALS.PAS
file.
Some of the other important procedures included the Prompt
procedure which put a line of text on the 23rd line of the screen.
This was basically used to send some sort of message to the user.
There was also a beep procedure that went well with the prompt
procedure.
Then a Query procedure was added, which worked just like the
prompt procedure, but then read in a line of text from the user so
that a response could be returned. Then there is the Error_Display
procedure which is like Prompt, but in a different color to help grab
the user's attention.
The next thing on our list is the InitGame procedure. Basically
this is called everytime you start a new game. This initializes all
the conditions in the game and finds out whether the user want's
expert or novice mode. Also initial positions for all the pieces are
set. This should all be very straight forward.
One thing you might notice in the InitGame procedure is the very
beginning. The code starting with SetFillStyle(8, Blue) through
OutTextXY(370,220,'(C) ... this is the opening screen. Using the
simple Gothic font and some easy to use graphic procedures, Turbo
Pascal let's you do some very nice opening screens.
The Convert_Rank and Convert_File procedures were used to convert
the board positions (A1-H8) to screen pixel positions. This was
necessary since we were using a board that was 8x8 in concept, but
actually several hundred pixels in length and height.
The Display_Move_History updates the Move_History that is shown
on the screen. Basically it's a list of all move made since the game
began. This, again, is done in a fairly straight-forward way, except
for one thing. I used the assignment: directvideo:= false; before
doing any screen writes. This allows you to write text into a graphics
screen.
The show_text procedure, like the Display_Move_History turns
directvideo off. All it really does is displays the text on the
screen, other than the move_history. It is straight-forward and easy
to follow.
The next unit I want to cover is the Draw_Pcs unit. This one is
very simple to follow. It was more of a tedious routine to put
together than a complicated routine. Basically you sit and draw your
pieces one line at a time and keep looking at them on the screen until
they look good. In this case, the pieces were drawn by Mark Friedman,
- 16 -
who spent a lot of time drawing the pieces out.
I would like to point out one improvement we encountered when
writing this. Originally Mark wrote the procedure using for-do loops
and the putpixel procedure to draw the pieces. This turned out to be
very slow, so we ended up replacing all the putpixels and for-do loops
with lineto procedures. This resulted in a huge time savings. You'll
notice that the squares are still drawn using the putpixel routine. We
left this that way for the visual effects it provides. That's really
all there is to the Draw_Pcs.
The last unit we will be discussing in this issue is the KB1
unit. This Get_Keyboard procedure at the end of the unit is the
center-piece for the entire program. It is where the user's input is
handled. It checks to make sure certain keystrokes are valid for the
current mode. (Expert or Novice). It takes care of check-mate.
Basically the routine is centered around a repeat-until loop that
reads the keyboard input and then uses a case statement to process
different requests from the user.
Going back to the beginning of the unit we have the Arrow_Moves
procedure. This reads in the keypad arrows and uses them for cursor
movement in the game. It also checks to make sure the user stays on
the board and doesn't try to roam off.
Following that are two Beep routines (Beep2 & Beep4) These,
looking back, probably belong in the Globals unit with Beep, but we
will discuss that later when we talk about ways to improve the
program.
The Select_A_Piece procedure changes the color of a piece on the
board after the user has selected that piece to be moved. Following
that is DeSelect which then turns the piece back to it's previous
color.
The Check_Move_Piece procedure takes care of picking up and
moving pieces on the board. IT calls the logic procedures(Legal_Move)
to see if the moves are legal. We'll get involved in the logic part in
a later issue.
The Check_Legal_Piece checks to see if the piece the user has
selected is a piece on his side.
And the final procedure is the Handle_New_Game_Request which
basically re-initializes the game state and board.
Now, I know what you're thinking. You think I've shot through
this program without a whole lot of explanation and you're right. I
want to give you a quick overview of everything first and when we've
covered the entire program I will back-track and go in-depth into
parts we've only touched on now.
- 17 -
The entire program is very large and represents a lot of work
done by six people. There are errors and the code is not perfect, but
I believe that showing some of the mistakes we made can help prevent
you from making the same ones.
- 18 -
Searching
By Kelly Schwarzhoff
Internet schwarzhoff@f103.n914.z8.rbbs-net.org
Rbbs-Net 8:914/103
Fidonet 1:125/41
For anyone who is planning on taking a Pascal class there comes a
time when he/she must make a search program. For me that happened two
weeks ago in my high school AP Computer Science class. I had to make
a program that would search for a certain inventory part and print out
the inventory or "not found" if there are none. In the following
examples I assume you're using the following type definitions:
list=record
id:integer;
inv:integer;
end;
parts=array[1..5000] of list;
I also assume you have defined variable a to be a variable of
type parts (this is the raw data) and a variable count to be of type
integer (this is how many different parts there actually are).
Finally, I expect the id #'s to be pre-sorted from smallest to biggest
(see PNL #3 for an excellent article on sorting).
There are two main type of searches. The easiest way is a
sequential sort that simply goes through the list and will search for
a particular number.
Although the sequential sort is nice and easy, it is extremely
slow. Luckily for us there is a faster (and harder) way called a
binary search. Binary search requires the data to be sorted in order
of the item-type you are searching for. The procedure will set two
variables (called b and z in the program) to be at each end of the
parts array. It will then create a variable mid that will equal the
middle of b and z ( (b+z) div 2). It will then check to see what the
value of the id number of mid is (a[mid].id) and make an if then else
if check. If the number is equal to the number you're looking for
(a[mid].id=look) then you found it and it will return that number. If
the mid is greater than the number then it is in the lower side of
your list in which case you set the right hand side variable to right
below mid. If the mid is less than the number then the number you're
looking for is in the lower side of your list in which case you set
the left-hand variable to right above mid. This continues until the
two variables either collide or you find the variable. If the id #
you're looking for is never found the function will return -1.
For those of you who want the technical speeds of the searches,
sequential is on average an O(N/2) search and binary is an O(log2 N)
search.
- 19 -
If you have any questions feel free to contact me at the listed
network addresses.
{Editor's Note: Two files included with PNL006.ZIP are SEQUENT.PAS and
BINARY.PAS. These are related to this article.
A note about the author. Kelly Schwarzhoff is a sophmore in high
school. I bring this up, mostly to point out that the articles in this
newsletter are not written by PhDs in Computer Science. One of the
most common compliments about the newsletter is that it's written by
regular people like you and me, so go ahead and send in an article!}
- 20 -
For Beginners
by Bob Gowans
In this issue of PNL I thought we could develop a programming
project based on the knowledge we have obtained from previous issues
of PNL. As wordprocessors are familiar to us all I have decided to
build our project around text manipulation. First, it will involve
counting the number of words in a line of text. Second, calculating
the length of a line of text. Both procedures are obviously simplified
versions of those a commercial wordprocessor would make use of to
invoke word wrap and set text between left and right margins, however
the principle of problem solving, program design and program coding is
the same whether the project is large or small.
Before running to the nearest PC and hastily churning out the
code for our project lets stop and think a while about program design
and any constraints we may have to impose on the program. Program
constraints will be given, bearing in mind the knowledge that has been
obtained from the last five articles in the beginners column.
Obviously there may be better ways to implement the program than the
way suggested, but we must write the program within the confines of
what we know. It is not unusual for programmers to have constraints
placed upon them - for example financial constraints could influence
the implementation of one design over another.
Wordcount/Line length -
A line of text is to be processed, each word is followed by a
single space including the last word in the line. The line is
processed to determine the total number of words. We can assume that
the line of text is entered by the user from the keyboard and at least
one word will be entered. The result, the total number of words in a
line is printed to the screen. In addition a count of the total number
of characters in the line, including spaces, is calculated. A line
length is then calculated in inches based on an assumption that 10
characters will be one inch in length.
The design for this process must involve a loop that scans the
text, looking for the single space between the words, it must also
involve a counter to keep track of the number of spaces. How will the
loop be terminated? ie. as the line of text is scanned how will the
program know it has reached the end of the line. We can make the
decision that the end of the line will include a space then a special
character, % (percent). Also,the loop must make a count of all the
characters in the line, excluding the final space and the special end
of line character (%).
Pseudo-code
1. read in first character
2. initialize variables updated in loop
- 21 -
3. loop while there are characters in the line of text
4. process characters
5. read in next character
6. loopend
7. calculate total number of words
8. calculate length of line of text
9. write out total number of words
10. write out length of text
Assuming that the character to be processed is held in the
variable called character, step 3 can be refined as follows :
3.1 loop while character <> percent sign
The end of each word is marked by a space so if the character
being processed is a space we can update a variable spacecount by 1,
if the character being processed is anything else we update the
variable lettercount by one.
4.1 if character = space
4.2 then
4.3 update spacecount by one
4.4 else
4.5 update lettercount by one
4.6 ifend
The variables spacecount and lettercount are initialized in step 2 and
are further refined below:
2.1 set spacecount to zero
2.2 set lettercount to zero
Step 7 does not really need refining as the total number of spaces is
equivalent to the total number of words in the line of text. When the
loop is terminated spacecount will contain the value for the total
number of words.
For the sake of clarity we will write step 7 as follows:
7.1 set totalwords to spacecount
Step 8 however, requires a bit more thought. The length of a line of
text in inches is given by the total number of characters divided by
10. The total number of characters would be calculated by adding
lettercount and spacecount minus one (the last space on the line is
not counted). This would give the total number of characters including
spaces. To complete the calculation the total number of characters is
divided by 10 and the result printed out, in inches, to 2 decimal
places.
8.1 set totalcharacters to lettercount + spacecount - 1
8.2 set linelength to totalcharacters/10
- 22 -
The final design is as follows:
1. read in first character
2.1 set spacecount to 0
2.2 set lettercount to 0
3.1 loop while character <> percent sign
4.1 if character = space
4.2 then
4.3 update spacecount by one
4.4 else
4.5 update lettercount by one
4.6 ifend
5. read in next character
6. loopend
7.1 set wordcount to spacecount
8.1 set totalcharacters to lettercount + spacecount - 1
8.2 set linelength to totalcharacters/10
9.1 writeout 'Total words in text line = ',totalwords
10. writeout 'Length of line of text = ',linelength,' inches'
Implementation of the design
The Pascal coding of the design should not present any problems
as we have covered most of the issues before. However I would like to
introduce constants into this particular code. Constants are used to
give a fixed value to an identifier. The system uses constants such as
Boolean values or the set of integer values, these are internal
constants set by the system. Constants can also be named by the user
as illustrated:
const
interest_rate = 0.15;
The Pascal reserved word CONST must come before the constant
definition. Constant definitions are placed, in order of program
sequence, before variable declarations as you will notice in the code
below. Notice also that the assignment symbol for a constant is simply
an equals (=) sign and not the Pascal assignment symbol (:=). The
advantages of using named constants are that you can make your code a
lot more readable if you use them wisely. When using constants it is
useful to bear in mind the possibility of your program being updated.
In the example above, the constant definition of interest_rate could
be used in a program that calculates loan repayments. We all know too
well how interest rates fluctuate so it is advantageous to declare
interest rate as a constant. When it comes to updating the program, as
a new interest rate is given, we simply have to change the constant
definition line. All references to interest_rate in the program are
then automatically updated, saving you a lot of work!
- 23 -
Constructing a data table for the design:
IDENTIFIER DESCRIPTION TYPE
space the space character ' ' constant
line_end the percent character '%' constant
cpi characters per inch constant
character characters input by user char
spacecount counter of space characters integer
lettercount counter of letter characters integer
total_chrs holds total number of
letters and spaces integer
total_words holds total number of words integer
line_length length in inches of 10
characters real
Here's the code to our design:
program process_line;
{By R.Gowans 7/2/91 - Manchester Polytechnic Didsbury}
{Calculates number of words in a line of text as input by the user and
outputs the result to the screen. Calculates line length given there
are 10 characters per inch and outputs results to screen.}
const
space = ' ';
line_end = '%';
cpi = 10;
var
character : char;
spacecount,lettercount,total_chrs,total_words : integer;
line_length : real;
begin
writeln('Input a line of text, each word must be followed by a');
writeln('space,the line must be terminated by a pct sign (%)');
writeln;
write('Enter line > ');
read(character);
spacecount := 0;
lettercount := 0;
while character <> line_end do
begin
if character = space
then
spacecount := spacecount + 1
else
lettercount := lettercount + 1;
- 24 -
read(character)
end;
total_words := spacecount;
total_chrs := lettercount + spacecount - 1;
line_length := total_chrs/cpi;
writeln;
writeln('Total words in text line = ',total_words);
writeln;
writeln('Length of line = ',line_length:4:2,' inches')
end.
You will have probably noticed that there are a lot of 'What
if's???' about this program and if it has raised some questions in
your mind, well that is the intention. Some of the questions I hope
you may be asking yourself are:
1. What if the character read in is not a space, letter or a
percent sign?
2. What if the first character is a percent symbol?
3. What if there is more than one space between the words?
You can probably think of more questions which will reveal the
weaknesses of this program and it is fitting to be critical in order
to make the best possible,positive modifications to a program. I will
leave you to think about the question of data checking, it is an
essential part of any non-trivial program. Try predicting what the
program will do when you enter unusual data then try it out! A word of
warning - be sure you know how to break out of a program if you are
attempting the unpredictable, the usual sequence on a PC is to hold
down the CTRL key and press c or break.
- 25 -
Conclusion
Well, that about wraps up this rather hastily assembled issue of
the newsletter. I'm sorry for taking so long and I hope to be able to
do another issue before summer, but I can't promise anything. Either
way, I plan on trying to make up for it this summer by putting out 4
or 5 issues over the summer.
I really need your help. The readers who contribute really make
this happen and unless I receive articles from the user, it's going to
take longer and longer between issues. Eventually, if I have to carry
the load of writing the entire newsletter, it will fade away. I don't
want to see that happen, so please try to help out. Try to set aside a
few hours on a weekend and put something together for us here.
I'm still not too sure about what's in-store for the next issue.
I haven't really thought about it too much. I will do more on Chess-
Ter and surely Bob will be back with For Beginners. I hope to have
Richard back writing his articles on Graphics in TP. I haven't
published anything about TP6.0 because frankly, I haven't had a chance
to use it myself, yet, and I haven't received anything from my readers
on it, so if there are those of you that have gotten familiar with
TP6.0, I'd sure like to see some reviews.
Oh well, enough begging on my part. I'll leave the rest up to you
guys. I'm off to complete what is left of my vacation. Hope you
enjoyed the issue and I'll write to you in the next issue.
_Pete Davis
- 26 -
The Pascal NewsLetter is Copyrighted by Pete Davis. Articles
submitted by others are the property of the authors and are used
with permission. They may not be treated seperately from this
newsletter without the author's permission and thus maintain all
distribution rules of the newsletter as a whole. It may be freely
distributed in un-modified form, but no charge whatsoever may
be incurred on the recipient. All code is provided 'as-is' with no
guarantees whatsoever.
The Pascal NewsLetter can be obtained from the following
locations:
GEnie : General Electric Network for Information
Exchange. It is located in the IBMPC
filelist.
Simtel : Internet address: 26.2.0.74 or
Simtel20.ARPA It is located in the
<MSDOS.PASCAL> directory.
Programmer's Forum : This is the home of the PNL.
Each issue can be found in
the PNL directory from the main area.
The number is on the title page.
If you would like to receive back issues of PNL directly
from me, send a diskette and $2.00 for shipping. Don't forget
to include your address.
Send your order to:
Peter Davis
4851 Brandywine St. NW
Washington, DC 20016
If you are a SysOp that will regularly carry PNL and would
like to have your bulletin board listed as such, here, send me a
message either by postal mail or at one of the electronic addresses
given on the title page, with your bulletin board's name, phone
number, and your name.
- 27 -
The Pascal NewsLetter Distribution List
BBS | Fidonet Address | Phone #
--------------------------+----------------------+---------------
The Bored | 1:397/3 | (512) 303-0471
Classic City BBS | 1:370/10 | (404) 548-0726
Thieve's World BBS | 1:106/805 | (713) 463-8053
Hippocampus BBS | 1:141/205 | (203) 484-4621
Rogers State College | 1:170/711 | (918) 341-8982
The Foxtrot BBS | 1:272/26 | (914) 567-1814
Turbo City BBS | 1:208/2 | (209) 599-7435
Austin IEMUG/MIDI BBS | 1:382/14 | (512) 258-0626
Laser Publishers | 1:170/403 | (918) 438-2749
Fargo RBBS-PC | 1:10/8 | (701) 293-5973
Momentary Lapse of Reason | | (704) 327-6361
The Demon's Den | | (508) 433-2702
The Cannibal Cafe | | (508) 840-6589
IBM Tech FIDO | | (508) 433-6491
The User Friendly BBS | | (704) 323-8223
KHIRON Software-Mail Only | 3:640/378 | 61-7-878-1194
Beyound Frontiers BBS | 2:230/101 | (+45)8694-1609
Collision Theory | | (703) 503-9441
Merlin's Mailbox | 2:245/39 |
Ed-Net 9600 | 1:153/734 | (604) 732-8877
EILC BBS | 1:3610/60 | (407) 676-2998
Polysyncronism BBS | | (708) 358-5104
(Name Unknown) | | (315) 592-7300
- 28 -